import json
from datetime import datetime
import random

from flask import Flask, render_template, jsonify, request, Blueprint # 导入 Blueprint
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import func
from snownlp import SnowNLP
import jieba

# --- 应用配置 ---
class Config:
    """应用配置基类"""
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    SQLALCHEMY_ECHO = True # 可以在生产环境中设为 False

class DevelopmentConfig(Config):
    """开发环境配置"""
    DEBUG = True
    HOSTNAME = "127.0.0.1"
    PORT = 3306
    USERNAME = "root"
    PASSWORD = "123456" # 建议使用环境变量或配置文件管理敏感信息
    DATABASE = "QQmusic"
    SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"

# --- 应用初始化 ---
app = Flask(__name__)
app.config.from_object(DevelopmentConfig) # 加载配置
db = SQLAlchemy(app)

# --- 数据库模型 ---
class Test(db.Model):
    __tablename__ = 'Test'
    id = db.Column(db.Integer, primary_key=True)
    CmId = db.Column(db.String(255)) # 建议指定 String 长度
    Avatar = db.Column(db.String(512))
    Nick = db.Column(db.String(255))
    Content = db.Column(db.Text) # 评论内容可能较长，使用 Text
    Location = db.Column(db.String(100))
    PubTime = db.Column(db.DateTime) # 使用 DateTime 类型存储时间
    PraiseNum = db.Column(db.Integer, default=0) # 点赞数应为整数

    def to_dict(self):
        return {
            'id': self.id,
            'CmId': self.CmId,
            'Avatar': self.Avatar,
            'Nick': self.Nick,
            'Content': self.Content,
            'Location': self.Location,
            # 确保 PubTime 是 datetime 对象才能格式化
            'PubTime': self.PubTime.strftime('%Y-%m-%d %H:%M:%S') if isinstance(self.PubTime, datetime) else str(self.PubTime),
            'PraiseNum': self.PraiseNum
        }

class User(db.Model):
    __tablename__ = 'user'  # 表名改为小写，与数据库保持一致
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(255))
    password = db.Column(db.String(255))
    avatar = db.Column(db.String(255))
    email = db.Column(db.String(255))
    phone = db.Column(db.String(255))
    address = db.Column(db.String(255))
    comment = db.Column(db.Text)  # 使用Text类型存储长文本

    def to_dict(self):
        """将对象转换为字典"""
        return {
            'id': self.id,
            'username': self.username,
            'avatar': self.avatar,
            'email': self.email,
            'phone': self.phone,
            'address': self.address,
            'comment': self.comment
        }

# --- 辅助函数 ---
def make_success_response(data=None, msg="操作成功"):
    """创建成功的 API 响应"""
    return jsonify({"code": 200, "data": data, "msg": msg})

def make_error_response(code=500, msg="操作失败"):
    """创建失败的 API 响应"""
    return jsonify({"code": code, "data": None, "msg": msg})

# --- API 蓝图定义 ---
api_bp = Blueprint('api', __name__, url_prefix='/api') # 创建蓝图，并设置 URL 前缀

# --- API 路由 ---
@api_bp.route('/daily_comment_count')
def daily_comment_count():
    """获取每日评论数量 (最近10天)"""
    try:
        # 添加错误处理和日志记录
        if not db.session:
            app.logger.error("数据库会话未初始化")
            return make_error_response(msg="数据库连接错误")

        # 使用更安全的查询方式
        result = db.session.query(
            func.date(Test.PubTime).label('PubDate'),
            func.count().label('count')
        ).filter(
            Test.PubTime.isnot(None)  # 过滤空值
        ).group_by(
            func.date(Test.PubTime)
        ).order_by(
            func.date(Test.PubTime).desc()
        ).limit(10).all()

        # 添加数据验证
        if not result:
            return make_success_response([], msg="暂无数据")

        # 更安全的数据处理
        result_list = []
        for row in result:
            try:
                if hasattr(row, 'PubDate') and row.PubDate:
                    result_list.append({
                        'date': row.PubDate.strftime('%Y-%m-%d'),
                        'count': row.count if row.count is not None else 0
                    })
            except (AttributeError, ValueError) as e:
                app.logger.warning(f"数据格式错误: {e}")
                continue

        result_list.reverse()  # 按日期升序排列
        return make_success_response(result_list)
    except Exception as e:
        app.logger.error(f"获取每日评论数量失败: {str(e)}")
        return make_error_response(msg=f"获取每日评论数量失败: {str(e)}")

@api_bp.route('/daily_sentiment_analysis')
def daily_sentiment_analysis():
    """获取每日评论情感分析 (最近10天)"""
    try:

        result = db.session.query(
            func.date(Test.PubTime).label('PubDate'),
            Test.Content
        ).all() # 筛选最近10天

        date_dict = {}
        for row in result:
            if not row.PubDate or not row.Content: # 跳过无效数据
                continue
            date_str = row.PubDate.strftime('%Y-%m-%d')
            if date_str not in date_dict:
                date_dict[date_str] = {'positive': 0, 'negative': 0, 'total': 0}

            try:
                s = SnowNLP(row.Content)
                if s.sentiments >= 0.5:
                    date_dict[date_str]['positive'] += 1
                else:
                    date_dict[date_str]['negative'] += 1
                date_dict[date_str]['total'] += 1
            except Exception as e:
                app.logger.warning(f"SnowNLP error for content ID {row.id if hasattr(row, 'id') else 'N/A'}: {e}")


        # 按日期排序并格式化输出
        sorted_dates = sorted(date_dict.keys())
        result_list = []
        for date_str in sorted_dates:
            data = date_dict[date_str]
            result_list.append({
                'date': date_str,
                'positive': data['positive'],
                'negative': data['negative'],
                'total': data['total']
                # 如果前端需要饼图格式，可以在这里转换或让前端处理
                # 'sentimentData': [
                #     {"name": "积极", "value": data['positive']},
                #     {"name": "消极", "value": data['negative']}
                # ]
            })

        # 如果需要严格限制为10条记录（即使数据不足10天）
        # return make_success_response(result_list[-recent_days:])
        return make_success_response(result_list)

    except Exception as e:
        app.logger.error(f"Error fetching daily sentiment analysis: {e}")
        return make_error_response(msg="获取每日情感分析失败")


@api_bp.route('/praise_ranking')
def praise_ranking():
    """获取点赞数量排行榜 (Top 10)"""
    try:
        # 确保 PraiseNum 是数值类型
        result = db.session.query(
            Test.Nick,
            Test.PraiseNum
        ).order_by(
            Test.PraiseNum.desc() # 直接按整数排序
        ).limit(10).all()

        result_list = [{'name': row.Nick, 'value': row.PraiseNum} for row in result]
        # 排行榜通常是降序，不需要 reverse
        return make_success_response(result_list)
    except Exception as e:
        app.logger.error(f"Error fetching praise ranking: {e}")
        return make_error_response(msg="获取点赞排行失败")


@api_bp.route('/location_distribution')
def location_distribution():
    """获取地区评论数占比"""
    try:
        result = db.session.query(
            Test.Location,
            func.count().label('count')
        ).group_by(
            Test.Location
        ).filter(Test.Location.isnot(None), Test.Location != '').all() # 过滤空地址

        result_list = [{'name': row.Location, 'value': row.count} for row in result]
        return make_success_response(result_list)
    except Exception as e:
        app.logger.error(f"Error fetching location distribution: {e}")
        return make_error_response(msg="获取地区分布失败")


@api_bp.route('/overall_sentiment')
def overall_sentiment():
    """获取整体情感分析"""
    try:
        # 注意：全表查询计算可能非常耗时，考虑抽样或预计算
        contents = db.session.query(Test.Content).yield_per(1000).all() # 分批获取减少内存占用
        positive_count = 0
        negative_count = 0
        total_count = 0

        for row in contents:
            if not row.Content:
                continue
            total_count += 1
            try:
                s = SnowNLP(row.Content)
                if s.sentiments >= 0.5:
                    positive_count += 1
                else:
                    negative_count += 1
            except Exception as e:
                 app.logger.warning(f"SnowNLP error during overall sentiment: {e}")


        result_list = [
            {"name": "积极", "value": positive_count},
            {"name": "消极", "value": negative_count}
        ]
        return make_success_response(result_list)
    except Exception as e:
        app.logger.error(f"Error fetching overall sentiment: {e}")
        return make_error_response(msg="获取整体情感分析失败")


@api_bp.route('/word_frequency')
def word_frequency():
    """获取高频词"""
    try:
        # 注意：全表查询计算可能非常耗时
        contents = db.session.query(Test.Content).yield_per(1000).all()
        word_counts = {}
        # 定义更全面的停用词列表，或从文件加载
        stopwords = set(['的', '我们', '你们', '你', '我', '了', '是', '在', '里', '为', '这', '就', '啊', '吧', '嗯', '哦', '哈', '嘿', '哼', '呀', '哇', '啦', '呢', '嘛', '了', '着', '过', '地', '得', '之', '与', '和', '或', '虽然', '但是', '因为', '所以', '而且', '并且', '而', '已', '以', '于', '其', '其他', '些', '个', '么', '那么', '如何', '怎样', '什么', '几', '谁', '被', '把', '将', '由', '从', '向', '往', '到', '对', '对于', '关于', '跟', '给', '替', '同', '除了', '沿', '顺', '按', '依照', '通过', '根据', '为了', '由于', '因为', '至于', '以致', '使得', '如果', '只要', '只有', '除非', '即使', '无论', '不管', '然而', '可是', '不过', '否则', '然后', '于是', '因此', '从而', '故', '所以', '总之', '看来', '例如', '比如', '即', '也就是', '或者', '还是', '以及', '及其', '甚至', '并且', '而且', '况且', '何况', '乃至', '包括', '不', '没', '没有', '别', '勿', '未', '不必', '不用', '除非', '否则', '的话', '左右', '上下', '前后', '里外', '附近', '周围', '本身', '自己', '彼此', '大家', '某', '各', '每', '任何', '一切', '所有', '全部', '全体', '整个', '各样', '各种', '若干', '多少', '之一', '之类', '等', '等等', '似的', '一样', '一般', '左右', '上下', '前后', '里外', '附近', '周围', '本身', '自己', '彼此', '大家', '某', '各', '每', '任何', '一切', '所有', '全部', '全体', '整个', '各样', '各种', '若干', '多少', '之一', '之类', '等', '等等', '似的', '一样', '一般', ' ', '\n', '\t', '...', '！', '？', '。', '，', '、', '；', '：', '“', '”', '‘', '’', '（', '）', '【', '】', '《', '》', '—', '～', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '=', '-', '[', ']', '{', '}', '|', '\\', '/', '<', '>', '.', ',', '?', '!', ';', ':'])

        for row in contents:
            if not row.Content:
                continue
            try:
                words = jieba.lcut(row.Content)
                for word in words:
                    word = word.strip()
                    # 过滤单字、数字、停用词
                    if len(word) > 1 and not word.isdigit() and word not in stopwords:
                        word_counts[word] = word_counts.get(word, 0) + 1
            except Exception as e:
                app.logger.warning(f"Jieba error during word frequency: {e}")


        # 按词频排序，取 Top N
        sorted_words = sorted(word_counts.items(), key=lambda item: item[1], reverse=True)
        top_n = 50 # 定义返回多少个高频词
        result_list = [{'name': k, 'value': v} for k, v in sorted_words[:top_n]]
        return make_success_response(result_list)
    except Exception as e:
        app.logger.error(f"Error fetching word frequency: {e}")
        return make_error_response(msg="获取高频词失败")


@api_bp.route('/login', methods=['POST']) # 只处理 POST 请求
def handle_login():
    """处理登录请求"""
    data = request.get_json()
    if not data:
        return make_error_response(code=400, msg='请求数据为空或格式错误')

    username = data.get('username')
    password = data.get('password')

    # 实际应用中应查询数据库验证用户
    # 从数据库查询用户信息
    user = User.query.filter_by(username=username).first()
    # 检查用户是否存在且密码是否匹配
    if user and user.password == password:  # 实际应用中应该使用加密密码
        # 登录成功，返回用户信息（除密码外）
        user_data = user.to_dict()
        user_data.pop('password', None)  # 移除密码字段
        return make_success_response(data=user_data, msg='登录成功')
    else:
        return make_error_response(code=401, msg='用户名或密码错误')


@api_bp.route('/data/list', methods=['GET'])
def search_comments():
    """查询所有评论数据"""
    try:
        # 获取分页参数
        page = request.args.get('page', 1, type=int)  # 当前页码
        per_page = request.args.get('per_page', 10, type=int)  # 每页数量
        
        # 获取筛选参数
        nick = request.args.get('nick', '')  # 昵称筛选
        location = request.args.get('location', '')  # 地点筛选

        # 构建基础查询
        query = Test.query

        # 添加筛选条件
        if nick:
            query = query.filter(Test.Nick.like(f'%{nick}%'))
        if location:
            query = query.filter(Test.Location.like(f'%{location}%'))

        # 按发布时间倒序排序
        query = query.order_by(Test.PubTime.desc())

        # 执行分页查询
        pagination = query.paginate(page=page, per_page=per_page, error_out=False)

        # 构建响应数据
        result = {
            'items': [item.to_dict() for item in pagination.items],
            'total': pagination.total,  # 总记录数
            'pages': pagination.pages,  # 总页数
            'current_page': pagination.page,  # 当前页
            'per_page': pagination.per_page  # 每页数量
        }

        return make_success_response(result)
    except Exception as e:
        app.logger.error(f"Error fetching comments: {e}")
        return make_error_response(msg="查询评论数据失败")


@api_bp.route('/data/update', methods=['PUT'])
def update_comment():
    """更新评论数据"""
    try:
        data = request.get_json()
        if not data:
            return make_error_response(code=400, msg='请求数据为空或格式错误')
        comment_id = data.get('id')
        # CmId = data.get("CmId")
        # Avatar = data.get('Avatar')
        # Nick = data.get('Nick')
        # Content = data.get('Content')
        # Location = data.get('Location')
        # PubTime = data.get('PubTime')
        # PraiseNum = data.get('PraiseNum')
        if not comment_id:
            return make_error_response(code=400, msg='评论ID为空')
        Test.query.filter_by(id=comment_id).update(dict(data))
        db.session.commit()
        return make_success_response(msg='数据更新成功!')
    except Exception as e:
        print(e)
        return make_error_response(msg="更新评论数据失败")

@api_bp.route('/data/add', methods=['POST'])
def add_comment():
    """添加评论数据"""
    try:
        data = request.get_json()
        data.append(('PubTime', datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
        if not data:
            return make_error_response(code=400, msg='请求数据为空或格式错误')
        new_comment = Test(**data)
        db.session.add(new_comment)
        db.session.commit()
        return make_success_response(msg='数据添加成功!')
    except Exception as e:
        print(e)
        return make_error_response(msg="添加评论数据失败")

# --- 注册蓝图 ---
app.register_blueprint(api_bp)

# --- 页面路由 (如果需要的话) ---
# 这些路由通常用于渲染 SPA 的入口 HTML 文件
# 如果你的前端是完全分离的，并且通过 Nginx 等部署，这些路由可能不需要

@app.route('/')
def serve_index():
    """服务前端主入口 (如果前端文件由 Flask 托管)"""
    # 通常返回 Vue/React 等构建后的 index.html
    # return render_template("index.html") # 假设前端构建文件在 templates 目录下
    return "<h1>Flask Backend is Running</h1><p>Access API at /api/...</p>" # 或者返回一个简单的状态信息

@app.route('/login', methods=['GET'])
def serve_login_page():
     """服务登录页面 (如果前端文件由 Flask 托管)"""
     # return render_template("admin/login.html")
     # 对于 SPA，通常所有前端路由都由 index.html 处理
     return serve_index() # 或者重定向到 /

@app.route('/admin', defaults={'path': ''})
@app.route('/admin/<path:path>')
def serve_admin_app(path):
    """服务后台管理应用的入口 (如果前端文件由 Flask 托管)"""
    # return render_template('admin/index.html')
    # 对于 SPA，通常所有前端路由都由 index.html 处理
    return serve_index() # 或者重定向到 /





if __name__ == '__main__':
    app.run()